package com.chuangke.admin.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chuangke.admin.constant.SysConstant;
import com.chuangke.admin.dao.SysMenuDaoService;
import com.chuangke.admin.entity.SysMenu;
import com.chuangke.admin.service.SysMenuService;
import com.chuangke.common.constant.CacheKeyConstants;
import com.chuangke.common.db.page.CkPageQuery;
import com.chuangke.common.exception.ChuangkeException;
import com.chuangke.framework.util.TreeUtils;

import cn.hutool.core.util.StrUtil;

@Service
public class SysMenuServiceImpl implements SysMenuService {

    @Autowired
    private SysMenuDaoService sysMenuDaoService;

    @Override
    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_MENU, key = "'" + CacheKeyConstants.SYS_MENU + "'")
    public void save(SysMenu record) {
        ((SysMenuService) AopContext.currentProxy()).doSave(record);
        level();
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void save(List<SysMenu> records) {
        throw new ChuangkeException("暂不支持的方法");
    }

    /**
     * 对数据进行分级，更新数据的level,is_last_level,data_domain
     */
    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_MENU, key = "'" + CacheKeyConstants.SYS_MENU + "'")
    public void level() {
        List<SysMenu> list = findTree();
        List<SysMenu> newList = new ArrayList<>();
        TreeUtils.level("", 0, list, newList);
        doUpdateBatch(newList);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_MENU, key = "'" + CacheKeyConstants.SYS_MENU + "'")
    public void doSave(SysMenu record) {
        sysMenuDaoService.saveOrUpdate(record);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_MENU, key = "'" + CacheKeyConstants.SYS_MENU + "'")
    public void doUpdateBatch(List<SysMenu> list) {
        sysMenuDaoService.updateBatchById(list);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_MENU, key = "'" + CacheKeyConstants.SYS_MENU + "'")
    @Override
    public void delete(SysMenu record) {
        List<SysMenu> children = findAllChildren(Collections.singletonList(record.getId()));
        List<SysMenu> allRecords = TreeUtils.treeToList(children);
        if (CollectionUtils.isEmpty(allRecords)) {
            return;
        }
        sysMenuDaoService.removeBatchByIds(allRecords);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_MENU, key = "'" + CacheKeyConstants.SYS_MENU + "'")
    @Override
    public void delete(List<SysMenu> records) {
        List<String> idList = records.stream().map(SysMenu::getId).collect(Collectors.toList());
        List<SysMenu> children = findAllChildren(idList);
        List<SysMenu> allRecords = TreeUtils.treeToList(children);
        if (CollectionUtils.isEmpty(allRecords)) {
            return;
        }
        sysMenuDaoService.removeBatchByIds(records);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CacheKeyConstants.SYS_MENU, key = "'" + CacheKeyConstants.SYS_MENU + "'")
    @Override
    public void deleteByIds(List<String> ids) {
        sysMenuDaoService.removeByIds(ids);
    }

    @Cacheable(cacheNames = CacheKeyConstants.SYS_MENU, key = "'" + CacheKeyConstants.SYS_MENU + "'")
    @Override
    public Map<String, SysMenu> findMap() {
        List<SysMenu> list = sysMenuDaoService.list();
        return list.stream().collect(Collectors.toMap(SysMenu::getId, a -> a));
    }

    @Override
    public List<SysMenu> findList() {
        Map<String, SysMenu> map = ((SysMenuService) AopContext.currentProxy()).findMap();
        return new ArrayList<>(map.values());
    }

    @Override
    public List<SysMenu> findTree() {
        List<SysMenu> depts = ((SysMenuService) AopContext.currentProxy()).findList();
        return TreeUtils.buildTree(depts);
    }

    @Override
    public List<SysMenu> findListWithChildren() {
        List<SysMenu> list = findTree();
        return TreeUtils.treeToList(list);
    }

    @Override
    public SysMenu findById(String id) {
        if (id == null) {
            return null;
        }

        Map<String, SysMenu> map = ((SysMenuService) AopContext.currentProxy()).findMap();
        return map.get(id);
    }

    @Override
    public List<SysMenu> findLastLevel() {
        List<SysMenu> treeList = findTree();

        List<SysMenu> lastLevelDeptList = new ArrayList<>();
        TreeUtils.findLastLevel(treeList, lastLevelDeptList);

        return lastLevelDeptList;
    }

    @Override
    public List<SysMenu> findByLevel(int level) {
        List<SysMenu> list = ((SysMenuService) AopContext.currentProxy()).findList();
        return TreeUtils.findByLevel(list, level);
    }

    @Override
    public List<SysMenu> findLastLevel(String deptId) {
        List<SysMenu> list = findListWithChildren();
        Map<String, SysMenu> map = list.stream().collect(Collectors.toMap(SysMenu::getId, a -> a));

        List<SysMenu> deptTreeList = new ArrayList<>();
        deptTreeList.add(map.get(deptId));

        List<SysMenu> lastLevelDeptList = new ArrayList<>();
        TreeUtils.findLastLevel(deptTreeList, lastLevelDeptList);
        return lastLevelDeptList;
    }

    @Override
    public List<SysMenu> findNextLevel(String deptId) {
        List<SysMenu> list = findListWithChildren();
        Map<String, SysMenu> map = list.stream().collect(Collectors.toMap(SysMenu::getId, a -> a));
        return map.get(deptId) == null ? null : map.get(deptId).getChildren();
    }

    @Override
    public List<SysMenu> findKinshipTree(Collection<String> deptIdList) {
        Map<String, SysMenu> noChildMap = ((SysMenuService) AopContext.currentProxy()).findMap();
        List<SysMenu> withChildrenList = findListWithChildren();
        return TreeUtils.findKinshipTree(noChildMap, withChildrenList, deptIdList);
    }

    @Override
    public SysMenu findParent(String deptId) {
        Map<String, SysMenu> noChildMap = ((SysMenuService) AopContext.currentProxy()).findMap();
        return TreeUtils.getParent(deptId, noChildMap);
    }

    @Override
    public List<SysMenu> findAllChildren(List<String> idList) {
        List<SysMenu> list = findListWithChildren();
        list = list.stream().filter(dept -> idList.contains(dept.getId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        List<SysMenu> result = new ArrayList<>();
        TreeUtils.foreachChildren(result, list);

        return result;
    }

    @Override
    public IPage<SysMenu> page(CkPageQuery page) {
        throw new ChuangkeException("暂不支持的方法");
    }

    @Override
    public List<SysMenu> findByUser(String userName) {
        if (StrUtil.isBlank(userName) || SysConstant.ADMIN.equalsIgnoreCase(userName)) {
            Map<String, SysMenu> map = ((SysMenuService) AopContext.currentProxy()).findMap();
            return new ArrayList<>(map.values());
        } else {
            return sysMenuDaoService.findByUserName(userName);
        }
    }

    /**
     * 获取菜单类型，0：获取所有菜单，包含按钮，1：获取所有菜单，不包含按钮
     */
    @Override
    public List<SysMenu> findTree(String userName, int menuType) {
        List<SysMenu> menus = findByUser(userName);
        if (menuType == 1) {
            menus = menus.stream().filter(menu -> menu.getType() != 2).collect(Collectors.toList());
        }

        List<SysMenu> menuResult = TreeUtils.buildTree(menus);
        sorted(menuResult);
        return menuResult;
    }

    @Override
    public List<SysMenu> findTree(String userName, int menuType, String fuzzy) {
        if (StrUtil.isBlank(fuzzy)) {
            return findTree(userName, menuType);
        }

        List<SysMenu> menus = findByUser(userName);
        menus = menus.stream().filter(menu -> (StrUtil.isBlank(fuzzy)
                        || (!StrUtil.isBlank(fuzzy) && (menu.getId().contains(fuzzy) || menu.getName().contains(fuzzy)))))
                .collect(Collectors.toList());

        List<String> menuIdList = menus.stream().map(SysMenu::getId).collect(Collectors.toList());

        List<SysMenu> menuTree = findKinshipTree(menuIdList);

        List<SysMenu> menuList = TreeUtils.treeToList(menuTree);
        if (menuType == 1) {
            menuList = menuList.stream().filter(menu -> menu.getType() != 2).collect(Collectors.toList());
        }

        List<SysMenu> menuResult = TreeUtils.buildTree(menuList);
        sorted(menuResult);
        return menuResult;
    }

    private void sorted(List<SysMenu> menus) {
        if (CollectionUtils.isEmpty(menus)) {
            return;
        }

        menus.sort(Comparator.comparing(SysMenu::getOrderNum));
        for (SysMenu menu : menus) {
            sorted(menu.getChildren());
        }
    }

}
